package minijvm.method;

import minijvm.attr.AttributeInfo;
import minijvm.attr.CodeAttr;
import minijvm.clz.ClassFile;
import minijvm.cmd.ByteCodeCommand;
import minijvm.constant.ConstantPool;
import minijvm.constant.UTF8Info;
import minijvm.loader.ByteCodeIterator;
import org.apache.commons.lang3.StringUtils;

import java.util.LinkedList;
import java.util.List;

/**
 * @author ChangLiang
 * @date 2020/4/13
 */
public class Method {

    private int accessFlag;
    private int nameIndex;
    private int descriptorIndex;

    private CodeAttr codeAttr;

    private ClassFile clzFile;


    public ClassFile getClzFile() {
        return clzFile;
    }

    public int getNameIndex() {
        return nameIndex;
    }
    public int getDescriptorIndex() {
        return descriptorIndex;
    }

    public CodeAttr getCodeAttr() {
        return codeAttr;
    }

    public void setCodeAttr(CodeAttr code) {
        this.codeAttr = code;
    }



    public Method(ClassFile clzFile,int accessFlag, int nameIndex, int descriptorIndex) {
        this.clzFile = clzFile;
        this.accessFlag = accessFlag;
        this.nameIndex = nameIndex;
        this.descriptorIndex = descriptorIndex;
    }

    public static Method parse(ClassFile clzFile, ByteCodeIterator iter){
        int accessFlag = iter.nextU2ToInt();
        int nameIndex = iter.nextU2ToInt();
        int descIndex = iter.nextU2ToInt();
        int attribCount = iter.nextU2ToInt();
//		System.out.println("method attriubte count:"+attribCount);
        Method method = new Method(clzFile,accessFlag,nameIndex,descIndex);
        for (int i = 0; i < attribCount; i++) {
            int attrNameIndex = iter.nextU2ToInt();
            String name = clzFile.getPool().getUTF8String(attrNameIndex);
//			System.out.println("attrNameIndex:"+attrNameIndex+"\tname:"+name);
            switch (name){
                case AttributeInfo.CODE:{
                    CodeAttr attr = CodeAttr.parse(clzFile, iter);
                    method.setCodeAttr(attr);
                    break;
                }
                default:{
                    throw new RuntimeException("Unimplement attribute "+name);
                }
            }
        }
		/*
		if(attribCount > 0){
			throw new RuntimeException("Method Attribute has not been implemented");
		}
		*/
        return method;
    }

    public ByteCodeCommand[] getCmds() {
        return this.getCodeAttr().getCmds();
    }

    @Override
    public String toString() {

        ConstantPool pool = this.clzFile.getPool();
        StringBuilder buffer = new StringBuilder();

        String name = ((UTF8Info)pool.getConstantInfo(this.nameIndex)).getValue();

        String desc = ((UTF8Info)pool.getConstantInfo(this.descriptorIndex)).getValue();

        buffer.append(name).append(":").append(desc).append("\n");

        buffer.append(this.codeAttr.toString(pool));

        return buffer.toString();
    }

    /**
     * 获取方法签名上的参数列表
     * @return
     */
    public List<String> getParameterList() {
        // 拿到方法的签名,e.g. (Ljava/util/List;Ljava/lang/String;II)V
        String paramAndReturnType = this.getParamAndReturnType();
        // 去掉括号
        int first = paramAndReturnType.indexOf("(");
        int last = paramAndReturnType.indexOf(")");
        String param = paramAndReturnType.substring(first + 1, last);

        List<String> list = new LinkedList<>();
        if (StringUtils.isEmpty(param)) {
            return list;
        }
        while (StringUtils.isNotEmpty(param)) {
            int pos = 0;
            if (param.charAt(pos) == 'L') {
                int end = param.indexOf(";");
                if (end == -1) {
                    throw new RuntimeException("param error: " + param);
                }
                list.add(param.substring(0, end));
                param = param.substring(end + 1);
            } else if (param.charAt(pos) == 'I') {
                list.add("I");
                param = param.substring(pos + 1);
            } else {
                throw new RuntimeException("unsupported type: " + param);
            }
        }
        return list;
    }

    private String getParamAndReturnType() {
        UTF8Info info = (UTF8Info) clzFile.getPool().getConstantInfo(descriptorIndex);
        return info.getValue();
    }
}
